

#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/if_arp.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static void  _dump(const void * data, int size)
{
    const char * buffer = (char *)data;
    int         i, j, t;
    for(i=0; i<size; i+=16)
    {
        printf("%08x: ", i);
        t = 16;
        if(i + 16 > size)
            t = size - i;
        for(j=0; j<t; j++)
        {
            printf("%02x ", buffer[i+j] & 0xff);
        }
        for(j=t; j<16; j++)
        {
            printf("   ");
        }
        printf("\t");
        for(j=0; j<t; j++)
        {
            if(buffer[i+j] >= 36 && buffer[i+j] <= 126)
                printf("%c", buffer[i+j] & 0xff);
            else
                printf(".");
        }
        printf("\n");
    }
    return;
}

static const char * ETH = "eth0";

static int get_mac_and_ip(char * mac, char * ip)
{
    struct  ifreq       tmp;
    int                 sock;
    struct sockaddr_in  sin;
    int                 i;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock == -1)
    {
        perror("socket");
        return -1;
    }

    if(mac != NULL)
    {
        memset(&tmp, 0, sizeof(tmp));
        strncpy(tmp.ifr_name, ETH, sizeof(tmp.ifr_name)-1);
        if( ioctl(sock, SIOCGIFHWADDR, &tmp) < 0 )
        {
            perror("ioctl.SIOCGIFHWADDR");
            return -1;
        }
        for(i=0; i<6; i++)
        {
            printf("%02x", tmp.ifr_hwaddr.sa_data[i] & 0xff);
            if(i == 5)
                printf("\n");
            else
                printf(":");
        }
        memcpy(mac, tmp.ifr_hwaddr.sa_data, 6);
    }
    if(ip != NULL)
    {
        memset(&tmp, 0, sizeof(tmp));
        strncpy(tmp.ifr_name, ETH, sizeof(tmp.ifr_name)-1);
        if( ioctl(sock, SIOCGIFADDR, &tmp) < 0 )
        {
            perror("ioctl.SIOCGIFADDR");
            return -1;
        }
        memcpy(&sin, &tmp.ifr_addr, sizeof(sin));
        printf("%s\n", inet_ntoa(sin.sin_addr));
        strcpy(ip, inet_ntoa(sin.sin_addr));
    }
    close(sock);
    return 0;
}

typedef struct
{
    struct ethhdr ethhdr;
    unsigned int type:4;
    unsigned int ver:4;
    unsigned int code:8;
    unsigned int session:16;
    unsigned int length:16;
    unsigned char payload[1500];
} DiscoveryPacket;



int main()
{
    char                mac[6];
    char                ip[30];
    int                 optval = 1;
    in_addr_t           ipa;
    DiscoveryPacket     msg;
    struct sockaddr     addr;

    get_mac_and_ip(mac, ip);
    printf("mac:");
    _dump(mac, 6);
    ipa = inet_addr(ip);
    ipa = inet_addr("10.0.0.188");

    int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_DISC));
    if(s == -1)
    {
        perror("socket");
        return -1;
    }
    /*
    if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1)
    {
        perror("setsockopt");
        return -1;
    }
    */

    struct ifreq    ifr;
    strcpy(ifr.ifr_name, "eth0");
    if(ioctl(s, SIOCGIFINDEX, &ifr) != 0)
        perror("ioctl.SIOCGIFINDEX");
    printf("ifindex = %d\n", ifr.ifr_ifindex);

    struct sockaddr_ll  sll;
    memset(&sll, 0, sizeof(sll));
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex;
    sll.sll_protocol = htons(ETH_P_PPP_DISC);
    if(bind(s, (struct sockaddr *)&sll, sizeof(sll)) != 0)
        perror("bind");

    while (true) {
        memset(&msg, 0, sizeof(msg));
        int ret = recv(s, &msg, sizeof(msg), 0);
        if (ret <= 0)
            break;
        /*
        if (msg.ethhdr.h_proto != htons(ETH_P_PPP_DISC)) {
            continue;
        }
        */
        printf("recv ppp packet:\n");
        _dump(&msg, ret);


        struct ethhdr e;
        memcpy(&e, &msg.ethhdr, sizeof(e));
        
        memcpy(msg.ethhdr.h_source, mac, 6);
        memcpy(msg.ethhdr.h_dest, e.h_source, 6);
        msg.ethhdr.h_proto = e.h_proto; // 0x8863 htons(ETH_P_PPP_DISC);


        char hostuniq[1000] = {0};
        short int  uniqlen = 0;
        int  payloadlen = 0;
        if (*(unsigned int*)msg.payload == 0x00000101) {
            if (*(unsigned short int*)(msg.payload + 4) == 0x0301) {
                uniqlen = (int)(*(unsigned short int *)(msg.payload + 6));
                uniqlen = htons(uniqlen);
                memcpy(hostuniq, msg.payload + 8, uniqlen);
            }
        }
        if (uniqlen > 0) {
            unsigned short int tag_host_uniq = 0x0301;
            memcpy(msg.payload, &tag_host_uniq, 2);
            payloadlen += 2;
            msg.payload[2] = (uniqlen >> 8) & 0xff;
            msg.payload[3] = uniqlen & 0xff;
            payloadlen += 2;
            memcpy(msg.payload + 4, hostuniq, uniqlen);
            payloadlen += uniqlen;

            msg.payload[8] = 0x01;
            msg.payload[9] = 0x01;
            msg.payload[10] = 0x00;
            msg.payload[11] = 0x00;
            payloadlen += 4;
        }

        if (msg.code == 0x09) {
            printf("PADI!\n");
            const char * acname = "kazuki..";
            unsigned int aclen = (unsigned int)strlen(acname);
            msg.payload[12] = 0x01;
            msg.payload[13] = 0x02;
            msg.payload[14] = (aclen >> 8) & 0xff;
            msg.payload[15] = aclen & 0xff;
            memcpy(msg.payload + 16, acname, aclen);
            payloadlen += 4;
            while ((aclen % 4) != 0)
                aclen ++;
            payloadlen += aclen;

            const char * srvname = "mypppoe";
            int srvlen = (int)strlen(srvname);
            msg.payload[16 + aclen] = 0x01;
            msg.payload[17 + aclen] = 0x01;
            msg.payload[18 + aclen] = (srvlen >> 8) & 0xff;
            msg.payload[19 + aclen] = srvlen & 0xff;
            memcpy(msg.payload + 20 + aclen, srvname, srvlen);
            payloadlen += 4;
            while ((srvlen % 4) != 0)
                srvlen ++;
            payloadlen += srvlen;

            msg.code = 0x07;
        } else if (msg.code == 0x19) {
            printf("PADR!\n");

            msg.session = 0x1234;
            msg.code = 0x65;
        } else if (msg.code == 0xa7) {
            printf("PADT!\n");
        } else {
            printf("unknown!\n");
        }

        int t = payloadlen;
        while ((t % 4) != 0)
            t++;
        t += 4;
        memset(msg.payload + payloadlen, 0, t - payloadlen);
        msg.length = ntohs((unsigned short int)payloadlen);

        _dump(&msg, 20 + t);

        usleep(100000);
        sendto(s, &msg, 20 + t, 0, (struct sockaddr*)&sll, sizeof(sll));
    }
    return 0;
}


